<template>
  <canvas :width="width" :height="height" ref="canvasRef"></canvas>
  <br />
  <button @click="onReset">Reset</button>
  <br />
  <button @click="onTran">Transition</button>
  <br />
  <button @click="onRotate">Rotate</button>
  <br />
  <button @click="onScale">Scale</button>
  <br />
  都是相对webgl坐标做变换
</template>
<script lang="ts">
export default {
  name: "平移-旋转-缩放",
};
</script>
<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import * as GLMatrix from "gl-matrix";
const width = ref<number>(600);
const height = ref<number>(600);
const canvasRef = ref<HTMLCanvasElement>();
const webgl = ref<WebGLRenderingContext>();
const programV = ref<WebGLProgram>();
const transition = ref(GLMatrix.mat4.create());
const taskId = ref<number>(-1);

const veSoure = computed(() => {
  return `
    uniform mat4 viewP;
    uniform mat4 trans;
    attribute vec4 point;
    void main(void){
      gl_Position = trans * viewP * point;
      gl_PointSize = 1.0;
    }

  `;
});

const veShader = computed(() => {
  const gl = webgl.value;
  if (!gl) return;
  const shader = gl.createShader(gl.VERTEX_SHADER);
  if (!shader) return;
  gl.shaderSource(shader, veSoure.value);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    var info = gl.getShaderInfoLog(shader);
    throw "Could not compile WebGL program. \n\n" + info;
  }
  return shader;
});

const frSource = computed(() => {
  return `
    void main(void){
      gl_FragColor = vec4(1.0,0.0,1.0,1.0);
    }
  `;
});

const viewValue = computed(() => {
  const value = GLMatrix.mat4.create();
  GLMatrix.mat4.ortho(value, 0, width.value, height.value, 0, -1, 1);
  return value;
});

const frShader = computed(() => {
  const gl = webgl.value;
  if (!gl) return;
  const shader = gl.createShader(gl.FRAGMENT_SHADER);
  if (!shader) return;
  gl.shaderSource(shader, frSource.value);
  gl.compileShader(shader);
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    var info = gl.getShaderInfoLog(shader);
    throw "Could not compile WebGL program. \n\n" + info;
  }
  return shader;
});

onMounted(() => {
  const gl = canvasRef.value?.getContext("webgl");
  if (!gl) return;
  webgl.value = gl;
  if (!veShader.value) return;
  if (!frShader.value) return;

  const program = gl.createProgram();
  if (!program) return;
  programV.value = program;
  gl.attachShader(program, veShader.value);
  gl.attachShader(program, frShader.value);
  gl.linkProgram(program);
  gl.useProgram(program);

  const viewP = gl.getUniformLocation(program, "viewP");
  gl.uniformMatrix4fv(viewP, false, viewValue.value);

  const trans = gl.getUniformLocation(program, "trans");
  gl.uniformMatrix4fv(trans, false, transition.value);

  const points = new Float32Array([100, 100, 300, 200, 300, 300, 200, 300]);
  const pBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, pBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);

  const pointV = gl.getAttribLocation(program, "point");
  gl.enableVertexAttribArray(pointV);
  gl.vertexAttribPointer(
    pointV,
    2,
    gl.FLOAT,
    false,
    points.BYTES_PER_ELEMENT * 2,
    0
  );

  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.LINE_LOOP, 0, points.length / 2);
});

const onReset = () => {
  const gl = webgl.value;
  const program = programV.value;
  if (!gl || !program) return;
  cancelAnimationFrame(taskId.value);
  transition.value = GLMatrix.mat4.create();
  const trans = gl.getUniformLocation(program, "trans");
  gl.uniformMatrix4fv(trans, false, transition.value);

  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.LINE_LOOP, 0, 4);
};

const onTran = () => {
  const gl = webgl.value;
  const program = programV.value;
  if (!gl || !program) return;
  cancelAnimationFrame(taskId.value);
  transition.value = GLMatrix.mat4.create();

  const newTime = new Date().getTime();
  const render = () => {
    const type = Math.ceil((new Date().getTime() - newTime) / 1000) % 4;
    const V = 0.01;
    const tranv: [number, number, number] =
      type === 0
        ? [V, 0, 0]
        : type === 1
        ? [-V, 0, 0]
        : type === 2
        ? [0, V, 0]
        : [0, -V, 0];
    GLMatrix.mat4.translate(transition.value, transition.value, tranv);
    const trans = gl.getUniformLocation(program, "trans");
    gl.uniformMatrix4fv(trans, false, transition.value);

    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.LINE_LOOP, 0, 4);
    taskId.value = requestAnimationFrame(render);
  };

  taskId.value = requestAnimationFrame(render);
};

const onRotate = () => {
  const gl = webgl.value;
  const program = programV.value;
  if (!gl || !program) return;
  cancelAnimationFrame(taskId.value);
  transition.value = GLMatrix.mat4.create();

  const newTime = new Date().getTime();
  const render = () => {
    const type = Math.ceil((new Date().getTime() - newTime) / 4000) % 4;
    const V = 100;
    const tranv: [number, number, number] =
      type === 0
        ? [V, 0, 0]
        : type === 1
        ? [0, V, 0]
        : type === 2
        ? [V, V, 0]
        : [-V, V, 0];

    GLMatrix.mat4.rotate(
      transition.value,
      transition.value,
      (Math.PI * 2) / 120,
      tranv
    );
    // GLMatrix.mat4.rotateZ(
    //   transition.value,
    //   transition.value,
    //   (Math.PI * 2) / 120,
    // );

    const trans = gl.getUniformLocation(program, "trans");
    gl.uniformMatrix4fv(trans, false, transition.value);

    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.LINE_LOOP, 0, 4);
    taskId.value = requestAnimationFrame(render);
  };

  taskId.value = requestAnimationFrame(render);
};

const onScale = () => {
  const gl = webgl.value;
  const program = programV.value;
  if (!gl || !program) return;
  cancelAnimationFrame(taskId.value);
  transition.value = GLMatrix.mat4.create();

  const newTime = new Date().getTime();
  const render = () => {
    const type = Math.ceil((new Date().getTime() - newTime) / 2000) % 2;
    const tranv: [number, number, number] =
      type === 0 ? [1.01, 1.01, 1.01] : [0.99, 0.99, 0.99];

    GLMatrix.mat4.scale(transition.value, transition.value, tranv);

    const trans = gl.getUniformLocation(program, "trans");
    gl.uniformMatrix4fv(trans, false, transition.value);

    gl.clearColor(0, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.LINE_LOOP, 0, 4);
    taskId.value = requestAnimationFrame(render);
  };

  taskId.value = requestAnimationFrame(render);
};
</script>

<style></style>